package testAndSomeTries.motionDetection;

import java.awt.image.BufferedImage;
import java.util.Arrays;

/**
 * 
 * Motion detection of an object
 * @author HINB
 *
 */
public class ObjectTracking {
	
	private static final long serialVersionUID = 1;
	
	/* *********************************************************
	 * Attributes
	 * ******************************************************** */
	/**
	 * Position of the gravity center of the object
	 */
	private int[] _grav_coord;
	
	
	/* *********************************************************
	 * Constructor(s)
	 * ********************************************************* */
	/**
	 * Beginning of a set of treatments on the background and foreground images
	 * Update of the coordinates of the object
	 */
	public ObjectTracking(BufferedImage image_buf_bg, BufferedImage image_buf_fg)
	{		
			
		_grav_coord = new int[2];
		_grav_coord[0] = -1;
		_grav_coord[1] = -1;
				
		//Convert to gray scale
		ImagePix image_bg_gray = new ImagePix(image_buf_bg, "gray");
		ImagePix image_fg_gray = new ImagePix(image_buf_fg, "gray");	
		
		//Subtraction
		//TODO Leo change ca
		ImagePix image_sub = subtraction(image_bg_gray, image_fg_gray);
		
		//Morphological transformation
		ImagePix image_morph_bw = morphTransform(image_sub);
		
		//Motion test
		motionTest(image_morph_bw);
	}
	

	//TODO Leo change ca
	/* *********************************************************
	 * Subtraction between 2 images
	 * *********************************************************
	 */
	/**
	 * Subtraction of two gray ImagePix and threshold of the result
	 * @param gray_1 first operand of the subtraction
	 * @param gray_2 second operand of the subtraction
	 * @return the threshold ImagePix
	 */
	public ImagePix subtraction(ImagePix gray_1, ImagePix gray_2)
	{		
		int width_1 = gray_1.getWidth();
		int width_2 = gray_2.getWidth();
		int height_1 = gray_1.getHeight();
		int height_2 = gray_2.getHeight();
		
		ImagePix gray_thr = new ImagePix(width_1, height_1);
		
		int sub;
		
		if( (width_1 == width_2) && (height_1 == height_2))
		{		
			for(int j=0; j<height_1; j++)
			{
				for(int i=0; i<width_1; i++)
				{
					sub = gray_1.getPixel(i, j).getGray() - gray_2.getPixel(i, j).getGray();					
					if(java.lang.Math.abs(sub) < 50)
						gray_thr.setPixel(i, j, new Pixel(0));
					else
						gray_thr.setPixel(i, j, new Pixel(255));
				}
			}
			
			return gray_thr;
		}
		
		else		
			return gray_1;
	}
	
	
	/* *********************************************************
	 * morphTransform : list if image treatments
	 * *********************************************************
	 */
	/**
	 * Set of transformation of the image
	 * @param gray ImagePix to transform
	 * @return the transformed ImagePix
	 */
	public ImagePix morphTransform(ImagePix gray)
	{
		int width = gray.getWidth();
		int height = gray.getHeight();
		
		ImagePix gray_morph = new ImagePix(width, height);
		ImagePix gray_ero = new ImagePix(width, height);
		ImagePix gray_dil = new ImagePix(width, height);
		
		gray_ero = erosion(gray);
		gray_dil = dilatation(gray_ero);
		gray_morph = reconstruction(gray, gray_dil);
		
		return gray_morph;
	}
	
	 
	/* *********************************************************
	 * erosion : erode an image
	 * *********************************************************
	 */
	/**
	  * Erosion operation of an image
	  * @param gray ImagePix to erode
	  * @return the eroded ImagePix
	  */
	public ImagePix erosion(ImagePix gray)
	{		
		int width = gray.getWidth();
		int height = gray.getHeight();
		
		ImagePix gray_ero = new ImagePix(width, height);
		
		int up;
		int down;
		int left;
		int right;
		int center;
		
		for(int j=1; j<height-1; j++)
		{
			for(int i=1; i<width-1; i++)
			{			
				center = gray.getPixel(i, j).getGray();
				left = gray.getPixel(i-1, j).getGray();
				right = gray.getPixel(i+1, j).getGray();
				up = gray.getPixel(i, j-1).getGray();
				down = gray.getPixel(i, j+1).getGray();
				
				//Mask
				if( (center == 255) && (left == 255) && (right == 255) && (up == 255) && (down == 255))
					gray_ero.setPixel(i, j, new Pixel(255));
				else
					gray_ero.setPixel(i, j, new Pixel(0));
			}
		}
		return gray_ero;
	}
	
	
	/* *********************************************************
	 * dilatation : dilate an image
	 * *********************************************************
	 */
	/**
	 * Dilatation operation of an image
	 * @param gray ImagePix to dilate
	 * @return the dilated ImagePix
	 */
	public ImagePix dilatation(ImagePix gray)
	{
		int width = gray.getWidth();
		int height = gray.getHeight();
		
		ImagePix gray_dil = new ImagePix(width, height);
		
		int up;
		int down;
		int left;
		int right;
		int center;
		
		for(int j=1; j<height-1; j++)
		{
			for(int i=1; i<width-1; i++)
			{			
				center = gray.getPixel(i, j).getGray();
				left = gray.getPixel(i-1, j).getGray();
				right = gray.getPixel(i+1, j).getGray();
				up = gray.getPixel(i, j-1).getGray();
				down = gray.getPixel(i, j+1).getGray();
				
				//Mask
				if( (center == 255) || (left == 255) || (right == 255) || (up == 255) || (down == 255))
					gray_dil.setPixel(i, j, new Pixel(255));
				else
					gray_dil.setPixel(i, j, new Pixel(0));
			}
		}
		return gray_dil;
	}
	
	
	/* *********************************************************
	 * reconstruction : image treatment
	 * *********************************************************
	 */
	/**
	 * Reconstruction operation of an image
	 * @param gray_thr threshold image
	 * @param gray dilated image
	 * @return the reconstructed ImagePix
	 */
	public ImagePix reconstruction(ImagePix gray_thr, ImagePix gray_dil)
	{
		int width = gray_thr.getWidth();
		int height = gray_thr.getHeight();
		
		ImagePix gray_reco = new ImagePix(gray_dil);
		ImagePix gray_aux = new ImagePix(width, height);	
		
		int up;
		int down;
		int left;
		int right;
		int center;
		int center_dil;
		int center_aux;
		
		int change;
			
		do 
		{
			change = 0;
			
			for(int j=1; j<height-1; j++)
			{
				for(int i=1; i<width-1; i++)
				{	
					center = gray_thr.getPixel(i, j).getGray();
					left = gray_thr.getPixel(i-1, j).getGray();
					right = gray_thr.getPixel(i+1, j).getGray();
					up = gray_thr.getPixel(i, j-1).getGray();
					down = gray_thr.getPixel(i, j+1).getGray();
					
					center_dil = gray_dil.getPixel(i, j).getGray();
					center_aux = gray_aux.getPixel(i, j).getGray();
					
					if((center_dil == 255) && (center_aux == 0))
					{
						change = 1;
						gray_aux.setPixel(i, j, new Pixel(255));
						gray_reco.setPixel(i-1, j, new Pixel(left));
						gray_reco.setPixel(i, j, new Pixel(center));
						gray_reco.setPixel(i+1, j, new Pixel(right));
						gray_reco.setPixel(i, j-1, new Pixel(up));
						gray_reco.setPixel(i, j+1, new Pixel(down));
					}
				}
			}
		} while(change != 0);	
		
		return gray_reco;
	}
	
	
	/* *********************************************************
	 * motionTest : detect objects in movement
	 * *********************************************************
	 */
	/**
	 * Set of operation in order to detect motion
	 * @param gray ImagePix in which objects have to be found
	 */
	public void motionTest(ImagePix gray)
	{
		int width = gray.getWidth();
		int height = gray.getHeight();
		
		ImagePix gray_dil = new ImagePix(width, height);
		
		gray_dil = dilatation(gray);
		labelling(gray_dil);
	}
	
	
	/* *********************************************************
	 * labelling : detect objects in movement
	 * *********************************************************
	 */
	/**
	 * Detection of the position of the different objects
	 * @param gray ImagePix in which objects have to be found
	 */
	public void labelling(ImagePix gray)
	{
		int width = gray.getWidth();
		int height = gray.getHeight();
		
		int nbMaxObjects = 500; //Maximal number of detected objects
		int[] pTabLabel = new int[nbMaxObjects];
		Arrays.fill(pTabLabel, 0);
		
		int newLabel = 1;
		int nbLabel = 0;
		
		CoordLabel[] pCoord = new CoordLabel[50];
		
		ImagePix gray_label = new ImagePix(width, height);	
			
		
		//Treatment
		for(int j=1; j<height-1; j++)
		{
			for(int i=1; i<width-1; i++)
			{
				if(gray.getPixel(i, j).getGray() == 255)
				{
					if(gray.getPixel(i-1, j).getGray() == gray.getPixel(i, j-1).getGray())
					{
						if(gray.getPixel(i-1, j).getGray() == 0)
						{		
							if(gray.getPixel(i-1, j-1).getGray() == 255)
							{
								gray_label.setPixel(i, j, new Pixel(gray_label.getPixel(i-1, j-1).getGray()));								
							}
							else
							{
								gray_label.setPixel(i, j, new Pixel(newLabel));								
								pTabLabel[newLabel] = 1;			
								newLabel++;
							}
						}
						else
						{
							if(gray_label.getPixel(i, j-1).getGray() < gray_label.getPixel(i-1, j).getGray())
							{
								gray_label.setPixel(i, j, new Pixel(gray_label.getPixel(i, j-1).getGray()));
								pTabLabel[gray_label.getPixel(i-1, j).getGray()] = gray_label.getPixel(i, j-1).getGray();
								gray_label.setPixel(i-1, j, new Pixel(gray_label.getPixel(i, j-1).getGray()));							
							}
							else
							{
								gray_label.setPixel(i, j, new Pixel(gray_label.getPixel(i-1, j).getGray()));
								pTabLabel[gray_label.getPixel(i, j-1).getGray()] = gray_label.getPixel(i-1, j).getGray();
								gray_label.setPixel(i, j-1, new Pixel(gray_label.getPixel(i-1, j).getGray()));							
							}
						}
					}
					else if((gray.getPixel(i, j-1).getGray() != 0) || (gray.getPixel(i-1, j).getGray() != 0))
					{
						if(gray.getPixel(i, j-1).getGray() == 255)
						{
							gray_label.setPixel(i, j, new Pixel(gray_label.getPixel(i, j-1).getGray()));						
						}
						else
						{
							gray_label.setPixel(i, j, new Pixel(gray_label.getPixel(i-1, j).getGray()));
						}
					}
				}
			}
		}
		
		//Equivalent chart update
		for(int k=newLabel; k>0; k--)
		{
			if((pTabLabel[k] != 0) && (pTabLabel[k] != k))
			{
				for(int j=1; j<height-1; j++)
				{
					for(int i=1; i<width-1; i++)
					{
						if(gray.getPixel(i, j).getGray() == k)
						{
							gray_label.setPixel(i, j, new Pixel(pTabLabel[k]));
						}
					}
				}
				pTabLabel[k] = 0;
				nbLabel--;
			}		
		}
		
		//Calculation of labels quantity
		nbLabel += newLabel - 1;
		
		int k = 0;
		//Calculation of labels quantity
		//Initialization of labels
		for(int e=0; e<nbMaxObjects; e++)
		{
			if (pTabLabel[e] != 0) 
			{
				pCoord[k] = new CoordLabel();
				pCoord[k].setLabel(e); 
				k++;
			}
		}
				
		
		//Initialization of envelopes coordinates
		for(int i=0; i<nbLabel; i++)
		{
			pCoord[i].setXMin(0);
			pCoord[i].setXMax(0);
			pCoord[i].setYMin(0);
			pCoord[i].setYMax(0);
		}
		
	
		int temp = 0;
		int save = 0;
		//Memorization of envelopes coordinates
		for(int j=1; j<height-1; j++)
		{
			for(int i=1; i<width-1; i++)
			{
				for(int e=0; e<nbLabel; e++)
				{
					if(gray_label.getPixel(i, j).getGray() == pCoord[e].getLabel())
					{
						//xMin
						if ((pCoord[e].getXMin() > j) || (pCoord[e].getXMin() == 0))
							pCoord[e].setXMin(j - 1);
						
						//xMax
						else if ((j > pCoord[e].getXMax()) || (pCoord[e].getXMax() == width)) 			
							pCoord[e].setXMax(j + 1);
						
						//yMin
						if ((pCoord[e].getYMin() > i) || (pCoord[e].getYMin() == 0))	
							pCoord[e].setYMin(i - 1);
						
						//yMax
						else if ((i > pCoord[e].getYMax()) || (pCoord[e].getYMax() == width)) 				
							pCoord[e].setYMax(i + 1);	
					}
					
					//The biggest object will be the hand
					temp = (pCoord[e].getXMax() - pCoord[e].getXMin()) * (pCoord[e].getYMax() - pCoord[e].getYMin());
					
					if(temp > save)
					{
						save = temp;
						_grav_coord[0] = pCoord[e].getXMin() + (pCoord[e].getXMax() - pCoord[e].getXMin())/2;
						_grav_coord[1] = pCoord[e].getYMin() + (pCoord[e].getYMax() - pCoord[e].getYMin())/2;
					}
				}				
			}
		}	
	}
	
	
	/* *********************************************************
	 * getGrav : access to the object position
	 * *********************************************************
	 */
	/**
	 * Get the position of the principal object
	 * @return the position of the principal object
	 */
	public int[] getGrav()
	{
		return _grav_coord;
	}
}
